home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
lisp
/
eulisp
/
mpfeel.lha
/
MPFeel
/
Plurals
/
cmlisp.emc
< prev
next >
Wrap
Lisp/Scheme
|
1992-04-03
|
20KB
|
668 lines
#include "mp_arith.h"
#include "mp_type.h"
(defmodule cmlisp (standard0 plural cmlisp-ll) ()
(defclass mp-object ()
((context
initarg context
reader context)
(offset
initarg offset
reader offset))
predicate mp-object-p)
(defclass xec (mp-object)
()
constructor (allocate-xec context offset)
predicate xecp)
(defmethod generic-prin ((p xec) str)
(format str "#x(")
(mp-print (context p) (offset p) str)
(format str ")")
p)
(defmethod generic-write ((p xec) str)
(format str "#x(")
(mp-print (context p) (offset p) str)
(format str ")")
p)
; Most Actual operations on xecs are those done with the "everywhere"
; context of (on our machine) 512 elements. As we know we are always
; using this context we work with the offsets and the EverWhere-Context
; which is abbreviated to EW-Ctxt. We define some operations and contexts
; which work solely in this context and they have the EWC prefix. EveryWhere
; is used to distinguish "everywhere" xappings {->obj} from orfinary xappings
(setq EW-Ctxt (mp-make-context 512))
((setter the-context) EW-Ctxt)
(defun Init-EveryWhere (MasPar-Config)
(let ((Ids (mp-make-plural EW-Ctxt)))
(labels ((recurse (n)
(mp-set EW-Ctxt Ids n n)
(if (zerop n) Ids (recurse (- n 1)))))
(recurse MasPar-Config))))
(setq EW-Ofst (Init-EveryWhere 512))
(setq WhereNext 510)
(setq XectorLim 1)
(setq EW-Nil (mp-bang EW-Ctxt ()))
(setq EW-Zero (mp-bang EW-Ctxt 0))
(setq EW-Unit (mp-bang EW-Ctxt 1))
(setq EW-Wild (mp-bang EW-Ctxt 9999))
(defun EW-Times (a b)
(mp-bin-op EW-Ctxt a b MP_TIMES))
(defun EW-Minus (a b)
(mp-bin-op EW-Ctxt a b MP_DIFFERENCE))
(defun EW-Plus (a b)
(mp-bin-op EW-Ctxt a b MP_PLUS))
(defun EW-Scan-Plus (a)
(mp-scan-op EW-Ctxt a MP_PLUS))
(setq WA-Ofst (EW-Plus EW-Ofst EW-Zero))
(setq EW-Shift (mp-set EW-Ctxt (EW-Plus EW-Ofst EW-Unit) 511 0))
; Below I have adopted the convention that any capitalised variable name
; holds te offset of a xec with the EveryWhere context
; where takes an object and returns a processor id (of sorts) which is
; used as the rendezvous address
(defcondition no-more-PEs ())
(defun where (o)
(let ((Here (EW-Plus EW-Zero EW-Zero)))
(cond
((and (eq (class-of o) integer) (< o WhereNext))
(if (> o XectorLim) (setq XectorLim o) o))
((mp-if EW-Ctxt (mp-eq EW-Ctxt WA-Ofst (mp-bang EW-Ctxt o)))
(progn
(mp-assign EW-Ctxt Here EW-Ofst)
(mp-fi EW-Ctxt)
(mp-ref EW-Ctxt (mp-scan-op EW-Ctxt Here MP_MAX) 511)))
((> WhereNext XectorLim)
(progn
(mp-fi EW-Ctxt)
(mp-set EW-Ctxt WA-Ofst WhereNext o)
(setq WhereNext (- WhereNext 1))
(+ WhereNext 1)))
(t (progn (mp-fi EW-Ctxt)
(error "Exhausted PE Locations" no-more-PEs))))))
(defun Intersect (Xecs)
(labels ((n-and (list-of-xecs)
(if (= (list-length list-of-xecs) 1) (car list-of-xecs)
(mp-and EW-Ctxt (car list-of-xecs)
(n-and (cdr list-of-xecs)))))
(n-car (list-of-xecs)
(if (cdr list-of-xecs) (n-car (cdr list-of-xecs)) ())
(mp-assign EW-Ctxt (car list-of-xecs)
(mp-car EW-Ctxt (car list-of-xecs)))))
(mp-if EW-Ctxt (if Xecs (n-and Xecs) EW-Ofst))
(if Xecs (n-car Xecs) ())
(EW-Plus EW-Unit EW-Zero)))
(setq it eq)
(defun reunite (Result Units)
(mp-else EW-Ctxt)
(mp-assign EW-Ctxt Result EW-Nil)
(mp-assign EW-Ctxt Units EW-Zero)
(mp-fi EW-Ctxt)
(let ((is-a (what-is-it Units))
(Enum (EW-Scan-Plus Units)))
(if (it is-a everywhere) (allocate-everywhere Result
(mp-ref EW-Ctxt Result 0))
(let* ((new-ctx (mp-make-context (mp-ref EW-Ctxt Enum 511)))
(To (EW-Plus (mp-bang EW-Ctxt (cm-start new-ctx))
(EW-Plus (EW-Minus Enum EW-Unit)
(EW-Times EW-Wild
(EW-Minus Units EW-Unit))))))
((if (it is-a xector) allocate-xector allocate-xapping) new-ctx
(mp-car new-ctx (cm-put EW-Ctxt EW-Ofst To new-ctx))
(mp-car new-ctx (cm-put EW-Ctxt Result To new-ctx)))))))
(defun what-is-it (Units)
(cond
((eq (mp-ref EW-Ctxt Units 511) 1) everywhere)
((zerop (mp-ref EW-Ctxt Units 0)) xapping)
(t (let ((Sftd-Units
(mp-set EW-Ctxt
(mp-car EW-Ctxt (cm-put EW-Ctxt Units EW-Shift EW-Ctxt))
0 1)))
(mp-if EW-Ctxt (mp-eq EW-Ctxt Units EW-Unit))
(mp-if EW-Ctxt (mp-eq EW-Ctxt Sftd-Units EW-Unit))
(let ((is-a-xapping (mp-else EW-Ctxt)))
(mp-fi EW-Ctxt)
(mp-fi EW-Ctxt)
(if is-a-xapping xapping xector))))))
; Xappings, the bit the user actually sees and works with and protects
; him from the nasty underlying operations
(defclass xapping ()
((context
initarg context
reader context)
(domain
initarg domain
reader domain)
(range
initarg range
accessor range))
predicate xappingp
constructor (allocate-xapping context domain range))
(defun xapping-domain-ref (x i)
(mp-ref EW-Ctxt WA-Ofst (mp-ref (context x) (domain x) i)))
(defun xapping-range-ref (x i)
(mp-ref (context x) (range x) i))
(defun xapping-length (x)
(mp-length (context x)))
(defmethod generic-write ((x xapping) str)
(let ((len (- (xapping-length x) 1)))
(labels ((print-pairs (i)
(format str "~a->~a" (xapping-domain-ref x i)
(xapping-range-ref x i))
(if (< i len) (progn (format str " ")
(print-pairs (+ i 1))) ())))
(format str "#X(")
(print-pairs 0)
(format str ")"))))
(defmethod generic-prin ((x xapping) str)
(let ((len (- (xapping-length x) 1)))
(labels ((print-pairs (i)
(format str "~a->~a" (xapping-domain-ref x i)
(xapping-range-ref x i))
(if (< i len) (progn (format str " ")
(print-pairs (+ i 1))) ())))
(format str "#X(")
(print-pairs 0)
(format str ")"))))
(defun list-to-xapping (pair-list)
(if (not (evenp (list-length pair-list)))
(error "Not an even number of elements" illegal-arg)
(let* ((new-context (mp-make-context (/ (list-length pair-list) 2)))
(new-domain (mp-make-plural new-context))
(new-range (mp-make-plural new-context)))
(labels ((recurse (pair-list i)
(if (cddr pair-list) (recurse (cddr pair-list) (+ i 1)) ())
(mp-set new-context new-domain i (where (car pair-list)))
(mp-set new-context new-range i (cadr pair-list))))
(progn
(recurse pair-list 0)
(allocate-xapping new-context new-domain new-range))))))
(defclass xector (xapping)
()
predicate xectorp
constructor (allocate-xector context domain range))
(defmethod generic-prin ((x xector) str)
(format str "#X[")
(mp-print (context x) (range x))
(format str "]")
x)
(defmethod generic-write ((x xector) str)
(format str "#X[")
(mp-print (context x) (range x))
(format str "]")
x)
(defcondition xector-too-big ())
(defun list-to-xector (list)
(if (>= (list-length list) WhereNext)
(error "Xector to big to accomodate" xector-too-big)
(let* ((new-context (mp-make-context (list-length list)))
(new-domain (mp-make-plural new-context))
(new-range (mp-make-plural new-context)))
(if (< (list-length list) XectorLim) ()
(setq XectorLim (list-length list)))
(labels ((recurse (list i)
(if (cdr list) (recurse (cdr list) (+ i 1)) ())
(mp-set new-context new-domain i i)
(mp-set new-context new-range i (car list))))
(progn
(recurse list 0)
(allocate-xector new-context new-domain new-range))))))
(defclass everywhere (xapping)
()
predicate everywherep
constructor (allocate-everywhere domain range))
(defmethod generic-prin ((x everywhere) str)
(format str "#X( ->~a)" (range x))
x)
(defmethod generic-write ((x everywhere) str)
(format str "#X( ->~a)" (range x))
x)
(defun rendezvous (x)
(if (everywherep x) (domain x)
(cm-put (context x) (range x) (domain x) EW-Ctxt)))
(defun what-pe (x i)
(let ((EW-Pes (cm-put (context x) EW-Ofst (domain x) EW-Ctxt))
(EW-Pe (EW-Plus EW-Zero EW-Zero)))
(mp-if EW-Ctxt EW-Pes)
(mp-assign EW-Ctxt EW-Pes (mp-car EW-Ctxt EW-Pes))
(if (mp-if EW-Ctxt (mp-eq EW-Ctxt WA-Ofst (mp-bang EW-Ctxt i)))
(progn
(mp-assign EW-Ctxt EW-Pe EW-Pes)
(mp-fi EW-Ctxt)
(mp-fi EW-Ctxt)
(mp-ref EW-Ctxt (mp-scan-op EW-Ctxt EW-Pe MP_MAX) 511))
(progn
(mp-fi EW-Ctxt)
(mp-fi EW-Ctxt)
()))))
(defcondition out-of-range ())
(defcondition illegal-arg ())
(defun xref (x i)
(if (xappingp x) (let ((EW-I (what-pe x i)))
(if EW-I (mp-ref EW-Ctxt (range x) EW-I)
(error "Index not in range" out-of-range)))
(error "Arg 1 not a xapping" illegal-arg)))
(defun xset (o x i)
(if (xappingp x) (let ((EW-I (what-pe x i)))
(if EW-I (progn (mp-set EW-Ctxt (range x) EW-I o) x)
(error "Index not in range" out-of-range)))
(error "Arg 2 not a xapping" illegal-arg)))
(defun xmod (v x i)
(if (xappingp x) (let ((EW-Index (where i))
(EW-Range (rendezvous x)))
(mp-set EW-Ctxt EW-Range EW-Index (list v))
(mp-if EW-Ctxt EW-Range)
(mp-assign EW-Ctxt EW-Range (mp-car EW-Ctxt EW-Range))
(reunite EW-Range (EW-Plus EW-Unit EW-Zero)))
(error "Arg 2 not a xapping" illegal-arg)))
; Primitives
; ==========
(setq pfun-table (make-table))
(defun add-pfun (name)
((setter table-ref) pfun-table name
(cons (last-function-name) (last-function-arglist))))
(setq psetter-table (make-table))
(defun add-psetter (name)
((setter table-ref) psetter-table name
(cons (last-function-name))))
(p-1-fn mp-un-op MP_NEGATE)
(add-pfun 'negate)
(p-2-fn mp-eq ())
(add-pfun 'eq)
(p-2-fn mp-cons ())
(add-pfun 'cons)
(p-1-fn mp-car ())
(add-pfun 'car)
(p-1-fn mp-cdr ())
(add-pfun 'cdr)
(p-1-fn mp-make-vector ())
(add-pfun 'make-vector)
(p-1-fn mp-vector-length ())
(add-pfun 'vector-length)
(p-2-fn mp-vector-ref ())
(add-pfun 'vector-ref)
(p-1-fn mp-test MP_CONS)
(add-pfun (quote consp))
(p-1-fn mp-test #x7fff)
(add-pfun (quote null))
(p-1-fn mp-test INTEGER)
(add-pfun (quote intp))
(p-1-fn mp-test MP_FLOAT)
(add-pfun (quote floatp))
(p-1-fn mp-test MP_VECTOR)
(add-pfun (quote vectorp))
(p-2-fn mp-bin-op MP_PLUS)
(add-pfun (quote binary-plus))
(p-2-fn mp-bin-op MP_PLUS)
(add-pfun (quote +))
(p-2-fn mp-bin-op MP_DIFFERENCE)
(add-pfun (quote binary-difference))
(p-2-fn mp-bin-op MP_DIFFERENCE)
(add-pfun (quote -))
(p-2-fn mp-bin-op MP_TIMES)
(add-pfun (quote binary-times))
(p-2-fn mp-bin-op MP_TIMES)
(add-pfun (quote *))
(p-2-fn mp-bin-op MP_DIVIDE)
(add-pfun (quote binary-divide))
(p-2-fn mp-bin-op MP_DIVIDE)
(add-pfun (quote /))
(p-2-fn mp-rel-op MP_GT)
(add-pfun (quote binary-gt))
(p-2-fn mp-rel-op MP_GT)
(add-pfun (quote >))
(p-2-fn mp-rel-op MP_LT)
(add-pfun (quote binary-lt))
(p-2-fn mp-rel-op MP_LT)
(add-pfun (quote <))
(p-2-fn mp-bin-op MP_REMAINDER)
(add-pfun (quote remainder))
(p-2-fn mp-and ())
(add-pfun (quote and))
(p-2-fn mp-or ())
(add-pfun (quote or))
(p-3-fn mp-vector-set ())
(add-psetter (quote vector-ref))
(p-2-fn mp-rplaca ())
(add-psetter (quote car))
(p-2-fn mp-rplacd ())
(add-psetter (quote cdr))
; There are a few lisp functions who work in parallel - this is a hack!
((setter table-ref) pfun-table 'progn (cons 'progn ()))
; Alpha
; =====
(defun rewire (form)
(cond
((consp form)
(cond
((eq (car form) 'quote) (rewire (cdr form)))
((eq (car form) (car function-name)) (cons (cadr function-name)
(rewire (cdr form))))
((eq (car form) 'bullet) (cadr form))
((eq (car form) 'setter) (table-ref psetter-table (cadr form)))
((eq (car form) 'if) (alpha-if (cadr form) (caddr form) (cadddr form)))
(t (cons (if (car form) (rewire (car form)) EW-Nil)
(rewire (cdr form))))))
((numberp form) (mp-bang EW-Ctxt form))
(form (if (memq form arg-list) (list 'mp-bang 'EW-Ctxt form)
(let ((alpha-fun (table-ref pfun-table form)))
(if alpha-fun (car alpha-fun) (list 'mp-bang 'EW-Ctxt form)))))
(t ())))
(defun alpha-if (bool then else)
(list 'let '((if-result (mp-make-plural EW-Ctxt)))
(list 'progn
(list 'if (list 'mp-if 'EW-Ctxt (rewire bool))
(list 'mp-assign 'EW-Ctxt 'if-result (rewire then)) ())
(list 'if (list 'mp-else 'EW-Ctxt)
(list 'mp-assign 'EW-Ctxt 'if-result (rewire else)) ())
'(mp-fi EW-Ctxt)
'if-result)))
(defun bulletify (name)
(list 'bullet name))
(defun Bind-args (args)
(labels ((make-binding (arg-name)
(list arg-name (list 'rendezvous arg-name)))
(recurse (list-of-args)
(if list-of-args (cons (make-binding (car list-of-args))
(recurse (cdr list-of-args))) ())))
(append (recurse args)
(list (list 'Units (list 'Intersect
(cons 'list args)))))))
(defun find-xappings (form)
(if (consp form)
(if (eq (car form) 'bullet)
(if ((setter table-ref) xapping-table (cadr form) t) ()
(setq xapping-list (cons (cadr form) xapping-list)))
(progn (find-xappings (car form))
(find-xappings (cdr form))))
xapping-list))
; (alpha if) is too compilacate and has to be created as a special case
(defmacro alpha (form)
(setq xapping-table (make-table))
(setq xapping-list ())
(setq arg-list ())
(setq function-name '(none))
(if (and (consp form) (not (eq (car form) 'setter)))
(cond
((eq (car form) `lambda)
(let ((args (cadr form)) (body (caddr form)))
(setq arg-list args)
`(lambda ,args
(let* ,(Bind-args (find-xappings body))
(reunite ,(rewire body) Units)))))
((eq (car form) `defun)
(let ((args (caddr form)) (body (caddr (cdr form))))
(setq function-name (list (cadr form) (gensym)))
((setter table-ref) pfun-table (car function-name)
(cons (cadr function-name) (caddr form)))
`(defun ,(cadr function-name) ,args
,(rewire body))))
(t
`((lambda ()
(let* ,(Bind-args (find-xappings form))
(reunite ,(rewire form) Units))))))
(let ((pfun (if (consp form) (table-ref psetter-table (cadr form))
(table-ref pfun-table form))))
(if pfun (let ((args (cdr pfun)))
`(lambda ,args
(let* ,(Bind-args args)
(reunite ,(rewire (cons form (mapcar bulletify args)))
Units))))
(allocate-everywhere (mp-bang EW-Ctxt form) form)))))
; Beta
; ====
(defun Build-map (index)
(let ((Map (mp-make-plural EW-Ctxt))
(len (- (mp-length (context index)) 1)))
(labels ((recurse (i)
(if (< i len) (recurse (+ i 1)) ())
(mp-if EW-Ctxt
(mp-eq EW-Ctxt EW-Ofst
(mp-bang EW-Ctxt
(where (xapping-range-ref index i)))))
(mp-assign EW-Ctxt Map
(mp-cons EW-Ctxt
(mp-bang EW-Ctxt i) Map))
(mp-fi EW-Ctxt)))
(recurse 0)
Map)))
(defun list-tail (l) (list-ref l (- (list-length l) 1)))
(defun b-rewire (form)
(cond
((consp form)
(cond
((eq (car form) 'quote) (b-rewire (cdr form)))
((eq (car form) 'bullet) (cadr form))
(t (cons (if (car form) (b-rewire (car form)) EW-Nil)
(b-rewire (cdr form))))))
((numberp form) (mp-bang EW-Ctxt form))
(form (if (memq form arg-list) form
(let ((alpha-fun (table-ref pfun-table form)))
(if alpha-fun (car alpha-fun) (list 'mp-bang 'EW-Ctxt form)))))
(t ())))
(setq Botch (mp-bang EW-Ctxt 'botch))
(defun p-default-combinator (a b) Botch)
(defun beta-internal (Range Map with)
(mp-if EW-Ctxt Range)
(mp-assign EW-Ctxt Range (mp-car EW-Ctxt Range))
(mp-fi EW-Ctxt)
(let ((Moved (mp-move EW-Ctxt Range EW-Ctxt Map))
(Result (mp-make-plural EW-Ctxt))
(Units (EW-Plus EW-Zero EW-Zero)))
(mp-if EW-Ctxt Moved)
(mp-assign EW-Ctxt Units EW-Unit)
(labels ((recurse (List CdrList)
(if (mp-if EW-Ctxt CdrList)
(mp-assign EW-Ctxt Result
(with (mp-car EW-Ctxt List)
(recurse CdrList
(mp-cdr EW-Ctxt CdrList)))) ())
(mp-else EW-Ctxt)
(mp-assign EW-Ctxt Result (mp-car EW-Ctxt List))
(mp-fi EW-Ctxt)
Result))
(reunite (recurse Moved (mp-cdr EW-Ctxt Moved)) Units))))
; Modification to mp-move - plural has to be pre-allocated !
(defun beta-internal (Range Map with)
(mp-if EW-Ctxt Range)
(mp-assign EW-Ctxt Range (mp-car EW-Ctxt Range))
(mp-fi EW-Ctxt)
(let ((Moved (mp-make-plural EW-Ctxt))
(Result (mp-make-plural EW-Ctxt))
(Units (EW-Plus EW-Zero EW-Zero)))
(mp-move EW-Ctxt Range EW-Ctxt Map Moved)
(mp-if EW-Ctxt Moved)
(mp-assign EW-Ctxt Units EW-Unit)
(labels ((recurse (List CdrList)
(if (mp-if EW-Ctxt CdrList)
(mp-assign EW-Ctxt Result
(with (mp-car EW-Ctxt List)
(recurse CdrList
(mp-cdr EW-Ctxt CdrList)))) ())
(mp-else EW-Ctxt)
(mp-assign EW-Ctxt Result (mp-car EW-Ctxt List))
(mp-fi EW-Ctxt)
Result))
(reunite (recurse Moved (mp-cdr EW-Ctxt Moved)) Units))))
(defun s-default-combinator (a b) 'botch)
(defun reduce (xapp with)
(let ((len (- (mp-length (context xapp)) 1))
(ctxt (context xapp))
(ofst (range xapp)))
(labels ((recurse (i)
(if (= i len) (mp-ref ctxt ofst i)
(with (mp-ref ctxt ofst i) (recurse (+ i 1))))))
(recurse 0))))
(defmacro beta args
(let* ((s-form (if (= (list-length args) 0) s-default-combinator
(car args)))
(p-form (if (= (list-length args) 0) p-default-combinator
(progn
(setq xapping-table (make-table))
(setq xapping-list ())
(setq arg-list ())
(if (consp (car args))
(let ((args (cadar args)) (body (caddar args)))
(setq arg-list args)
(list 'lambda args (b-rewire body)))
(car (table-ref pfun-table (car args))))))))
`(lambda args
(let ((s-with ,s-form)
(p-with ,p-form))
(if (cdr args)
(beta-internal (rendezvous (car args))
(Build-map (cadr args)) p-with)
(reduce (car args) s-with))))))
)
(defmacro compile (form)
(if (consp form)
(if (eq (car form) 'alpha) (macroexpand `,form)
(cons (compile (car form)) (compile (cdr form))))
form))
;; if
(alpha (if (< (bullet xap1) 5) (bullet xap1) (- 5 (bullet xap1))))
(let ((if-result (mp-make-plural EW-Ctxt)))
(progn
(if (mp-if EW-Ctxt
(setq simon (list-to-xapping '(cap 12 perihelion 3 BUCS 3)))
(setq don (list-to-xapping '(micro-automation 12 perihelion 30)))
(setq duncan (list-to-xapping '(IBM 12 perihelion 24)))
(setq bob (list-to-xapping '(micro-automation 18 Melbourne-Uni 3 BUCS 4)))
(alpha-if '(< (bullet simon) (bullet don)) '(cons (bullet simon) (bullet don))
'(cons (bullet don) (bullet simon)))
(macroexpand (alpha (if (< (bullet simon) (bullet don))
(cons (bullet simon) (bullet don))
(cons (bullet don) (bullet simon)))))
(macroexpand (p-if ((alpha <) don simon) ((alpha cons) don simon) ((alpha cons) simon don)))
(defmacro p-if (bool then else)
(setq xapping-table (make-table))
(setq xapping-list ())
(setq arg-list ())
`(let* ((bool-value (rendezvous ,bool))
(bool (let ((tmp (mp-assign EW-Ctxt
(mp-make-plural EW-Ctxt) bool-value)))
(mp-if EW-Ctxt bool)
(mp-assign EW-Ctxt bool (mp-car EW-Ctxt bool-value))
(mp-fi EW-Ctxt)))
(then (if (mp-if EW-Ctxt bool) (rendezvous ,then) EW-Nil))
(else (if (mp-else EW-Ctxt) (rendezvous ,else) EW-Nil))
(Units (Intersect (list bool then else))))
(reunite (let ((if-result (mp-make-plural EW-Ctxt)))
(mp-if EW-Ctxt bool)
(mp-assign EW-Ctxt if-result then)
(mp-else EW-Ctxt)
(mp-assign EW-Ctxt if-result else)) Units)))
(defprim p-list-length (p-list)
(if p-list (+ (p-list-length (cdr p-list)) 1)
0))
(defun p-list-length (p-list)
(if (mp-if EW-Ctxt p-list)
((lambda (a b) (mp-bin-op EW-Ctxt a b 610))
(p-list-length ((lambda (a) (mp-cdr EW-Ctxt a)) p-list))
(mp-bang EW-Ctxt 1))))
; This is very strange I mean list-length is now parallel so it has
; to be bulleted - or is it?